package com.ruoyi.common.websocket.server;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.utils.JSONUtil;
import com.ruoyi.common.utils.spring.SpringUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public final class WebSocketUtils {

	public static final Map<String, Session> clients = new ConcurrentHashMap<>();
	public static final Map<String, Object> commands = new ConcurrentHashMap<>();

	/**
	 * 向所有client推送消息
	 * 
	 * @param message 消息
	 */
	public static void publish(JSONObject message) {
		clients.forEach((sessionId, session) -> sendMessage(session, message));
	}

	/**
	 * 向client推送消息
	 * 
	 * @param username 接收消息用户名
	 * @param message  消息
	 */
	public static void sendMessage(String username, JSONObject message) {
		Session session = getSession(username);
		sendMessage(session, message);
	}

	/**
	 * @param username
	 * @return
	 */
	public static Session getSession(String username) {
		Session session = clients.get(username);
		return session;
	}

	
	/**
	 * 向client推送消息,并获取返回结果
	 * 
	 * @param username 接收消息的用户
	 * @param message  消息
	 * @return
	 */
	public static JSONObject sendSyncMessage(String username, JSONObject message) {
		Session session = getSession(username);
		if(session == null || !session.isOpen()) {
			return error(message, "网关不在线");
		}
		String key = getMessageKey(message);
		Thread thread = Thread.currentThread();
		try {
			commands.put(key, thread);
			boolean flag = sendMessage(session, message);
			if (!flag) {// 发送失败
				commands.remove(key);
				return error(message, "目标不在线");
			}

			// 阻塞获取
			int count = 0;
			while (true) {
				synchronized (thread) {
					Object target = commands.get(key);
					if (target instanceof JSONObject) {
						commands.remove(key);
						//log.info("远程调用返回,报文\n{}", target.toString());
						return (JSONObject) target;
					} else {// 结果未返回
						if (count == 1) {// 超时
							//log.warn("远程调用超时,报文\n{}", message.toJSONString());
							commands.remove(key);
							return error(message, "响应超时");
						}
						// wait释放对象锁,唤醒后继续往下执行
						Thread.currentThread().wait(15 * 1000L);
						count++;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return error(message, e.getMessage());
		}
	}

	private static JSONObject error(JSONObject message, String error) {
		JSONObject command = message.getJSONObject("command");
		int iCommand = command.getIntValue("iCommand") + 1;
		
		JSONObject res = new JSONObject();
		res.put("param", new JSONObject() {{
			put("iMessageCode", -1);
			put("szMessageString", error);
		}});
		JSONObject c = new JSONObject(command);
		c.put("iCommand", iCommand);
		res.put("command", c);
		return res;
	}

	/**
	 * 消息唯一性标识
	 * 
	 * @param message
	 * @return
	 */
	public static String getMessageKey(JSONObject message) {
		JSONObject command = message.getJSONObject("command");
		String szClientId = command.getString("szClientId");
		// int iCommand = command.getInteger("iCommand");
		// Date createTime = command.getDate("createTime");
		int iSeqNum = command.getInteger("iSeqNum");
		String key = szClientId + "_" + iSeqNum;
		return key;
	}
	/**
	 * 根据session获取用户名
	 * @param session
	 * @return
	 */
	public static String getUsername(Session session) {
		Iterator<String> it = clients.keySet().iterator();
		while(it.hasNext()) {
			String username = it.next();
			Session s = WebSocketUtils.clients.get(username);
			if(s == session) {
				return username;
			}
		}
		return null;
	}

	/**
	 * 发送给指定用户消息
	 *
	 * @param session 用户 session
	 * @param message 发送内容
	 */
	private static boolean sendMessage(Session session, JSONObject message) {
		if (session == null || session.getBasicRemote() == null) {
			return false;
		}
		final RemoteEndpoint.Basic basic = session.getBasicRemote();

		synchronized (session) {
			try {
				if(session.isOpen()) {
					//String text = JSON.toJSONString(message,SerializerFeature.WriteMapNullValue,SerializerFeature.WriteDateUseDateFormat);
					//String text = JSON.toJSONString(message,JSONWriter.Feature.WriteMapNullValue);
					String text = JSONUtil.toJSONString(message);
					//System.out.println(text);
					basic.sendText(text);
					return true;
				}else {
					log.error("id={}的客户端已经关闭,但未从第三方容器中移除",session.getId());
					//已经close的session再次close,不会触发onClose事件,需要手动执行onClose,并在onClose中实现幂等性(多次onClose仍然符合预期)
					//close(session);
					WebsocketServer server = SpringUtils.applicationContext.getBean(WebsocketServer.class);
					server.onClose(null, session, null);
					
				}
			} catch (Exception e) {
				e.printStackTrace();
				if (e.getMessage().contains(
						"The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method")) {
					close(session);
				}
			}
		}
		return false;
	}

	public static void close(Session session) {
		try {
			session.close();//触发onClose事件
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void sendGroupMessage(String group, JSONObject message) {
		clients.forEach((sessionId, session) -> {
			String g = (String) session.getUserProperties().get("group");
			if (g.equals(group)) {
				sendMessage(session, message);
			}
		});
		
	}
}
